import cookie from 'js-cookie';


type ObjectType = { [key: string]: string }

export interface UseLangType extends OptionsType {
  t: (key: string, values?: Record<string, any>) => string;
}

export interface OptionsType {
  locales: { [key: string]: ObjectType };
  defaultLocale?: string;
  locale?: ObjectType;
  localeId?: string;
  useLocales?: string[];
}

export const languageCodes = [
  "af", "sq", "am", "ar", "hy", "as", "ay", "az", "bm", "ba", "eu", "be", "bn", "bh", "bi", "bs", "br", "bg", "my", "ca", "ch", "ce", "ny", "zh", "cv", "kw", "co", "cr", "hr", "cs", "da", "dv", "nl", "dz", "en", "eo", "et", "ee", "fo", "fj", "fi", "fr", "fy", "ga", "gd", "gl", "lg", "ka", "de", "ki", "el", "gn", "gu", "ht", "ha", "he", "hz", "hi", "ho", "hu", "is", "ig", "id", "ia", "ie", "iu", "ik", "in", "ga", "it", "ja", "jv", "kl", "kn", "kr", "ks", "kk", "km", "ki", "rw", "ko", "ku", "kj", "lo", "la", "lv", "li", "ln", "lt", "lb", "mk", "mg", "ms", "ml", "mt", "mi", "mr", "mh", "mn", "na", "nv", "nd", "ne", "ng", "nb", "nn", "no", "ii", "oc", "oj", "or", "om", "os", "pa", "pi", "fa", "pl", "pt", "ps", "qu", "rm", "ro", "ru", "sm", "sg", "sa", "sc", "sr", "sn", "sd", "si", "sk", "sl", "so", "st", "es", "su", "sw", "ss", "sv", "tl", "ty", "tg", "ta", "tt", "te", "th", "bo", "ti", "to", "tn", "tr", "tk", "tw", "ug", "uk", "ur", "uz", "ve", "vi", "vo", "cy", "wa", "wo", "xh", "yi", "yo", "zu"
];

let I18nObject: Required<OptionsType> = {
  locale: {},
  locales: {},
  localeId: 'languageId',
  defaultLocale: 'zh',
  useLocales: languageCodes
}

const NotificationCenter = new Map(); // 消息中心

class Locale {
  language: string | undefined;
  dirNames: string[] = [];
  dirNameKeys: string = '';
  dirNameValues: {} = {};
  constructor(dirNames: string | string[], language?: string) {
    this.language = getLanguage(language);
    this.initDirNameKeys(dirNames);
    this.initDirNameValues();
  }
  initDirNameKeys(dirNames: string | string[]) {
    if (typeof dirNames === 'string') {
      dirNames = [dirNames]
    }
    // 根据首字母排序
    if (dirNames.length > 1) {
      dirNames.sort((a, b) => {
        let firstCharA = a.charAt(0).toLowerCase();
        let firstCharB = b.charAt(0).toLowerCase();

        if (firstCharA < firstCharB) {
          return -1;
        }
        if (firstCharA > firstCharB) {
          return 1;
        }
        return 0;
      });
    }
    this.dirNames = dirNames;
    this.dirNameKeys = dirNames.join("-");
  }
  initDirNameValues() {
    let locale = getLocale(this.language);
    for (let i = 0; i < this.dirNames.length; i++) {
      const name = this.dirNames[i];
      if (locale && locale[name]) {
        const curLocale = locale[name] || {};
        this.dirNameValues = { ...this.dirNameValues, ...curLocale }
      }
    }
  }
  update(language: string) {
    this.language = getLanguage(language);
    this.initDirNameValues()
  }
}

export const initI18n = (options: OptionsType) => {
  if (options.locales) I18nObject['locales'] = options.locales;
  if (options.useLocales) I18nObject['useLocales'] = options.useLocales;
  if (options.localeId) I18nObject['localeId'] = options.localeId;
  if (options.defaultLocale) I18nObject['defaultLocale'] = options.defaultLocale;
}

export const getModules = (modules: any, type: 'require' | 'glob') => {
  if (type == "require") {
    return getModuleByRequireContext(modules)
  }
  else if (type == "glob") {
    return getModuleByMetaGlob(modules)
  }
}

export const getModuleByMetaGlob = (modules: any) => {
  let resources: any = {};
  if (!modules) return {};

  Object.keys(modules).forEach((key: string) => {
    if (modules[key].default) {
      const urlArr = key.split('/');
      const matched = key.match(/([A-Za-z0-9-_]+)\./i);
      if (matched && matched.length > 1) {
        const dirName: string = urlArr.splice(-2, 1)[0]; // 获取目录名
        const lang = matched[1]; // 语言类型
        const context = modules[key].default;
        if (!resources[lang]) resources[lang] = {};
        resources[lang][dirName] = context;
      }
    }
  })

  return resources;
}

export const getModuleByRequireContext = (modules: any) => {
  let resources: any = {};
  if (!modules) return {};

  modules.keys().forEach((key: string) => {
    if (modules(key)) {
      const urlArr = key.split('/');
      const matched = key.match(/([A-Za-z0-9-_]+)\./i);
      if (matched && matched.length > 1) {
        const dirName: string = urlArr.splice(-2, 1)[0]; // 获取目录名
        const lang = matched[1]; // 语言类型
        const context = modules(key);
        if (!resources[lang]) resources[lang] = {};
        resources[lang][dirName] = context;
      }
    }
  })

  return resources;
}

export const getLocale = (language?: string) => {
  const languageId = getLanguage(language);
  const locales = getLocales();

  if (locales && languageId && locales[languageId]) {
    return locales[languageId]
  }

  return {}
}

export const getLocales = () => I18nObject.locales;

export const getLanguage = (language?: string) => {
  let langId = language ? language : cookie.get(I18nObject.localeId) || I18nObject.defaultLocale;
  if (!I18nObject.useLocales.includes(langId)) {
    console.warn('not found lang: ' + langId)
    langId = I18nObject.defaultLocale;
  }

  return langId;
};

export const setLanguage = (language: string) => {
  cookie.set(I18nObject.localeId, language);
  I18nObject.defaultLocale = language;
  // update watcher
  NotificationCenter.forEach((update) => update(language));
}

export const getLang = (key: string, language?: string) => {
  const local = getLocale(language);
  if (local && local[key]) {
    return local[key]
  }
  return key;
}

export const useLang = (dirNames: string | string[], language?: string): UseLangType => {
  if (Object.keys(I18nObject.locales).length == 0) {
    console.warn('Please initialize first and execute initI18n()')
  }

  // 初始化页面级别的 hooks
  const locale = new Locale(dirNames, language)

  // 通过消息中心管理所有的 locale
  if (!NotificationCenter.has(locale.dirNameKeys)) {
    NotificationCenter.set(locale.dirNameKeys, (lang: string) => locale.update(lang))
  }

  const t = (key: string, data?: ObjectType): string => {
    const lang: ObjectType = locale["dirNameValues"];
    if (!lang[key]) {
      console.warn('missing lang key: ' + key)
      return ''
    }
    let _lang = lang[key]
    if (data && Object.keys(data).length > 0) {
      Object.keys(data).map((item) => {
        _lang = _lang.replace(`{${item}}`, data[item])
      })
      return _lang
    } else return lang[key]
  }
  return {
    t,
    defaultLocale: locale.language,
    locale: locale.dirNameValues,
    locales: I18nObject.locales,
    localeId: I18nObject.localeId,
    useLocales: I18nObject.useLocales
  }
}

export const resetI18n = ({ removeCookie = true }) => {
  I18nObject = {
    locale: {},
    locales: {},
    localeId: 'languageId',
    defaultLocale: 'zh',
    useLocales: languageCodes
  }
  NotificationCenter.clear();
  if (removeCookie) {
    cookie.remove(I18nObject.localeId);
  }
}